package drds.plus.sql_process.optimizer.chooser;

import drds.plus.common.properties.ConnectionProperties;
import drds.plus.sql_process.abstract_syntax_tree.configuration.IndexMapping;
import drds.plus.sql_process.abstract_syntax_tree.configuration.TableMetaData;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.Item;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.Filter;
import drds.plus.sql_process.optimizer.OptimizerContext;
import drds.plus.sql_process.optimizer.cost_esitimater.CostEsitimaterFactory;
import drds.plus.sql_process.optimizer.cost_esitimater.statistics.TableIndexStatisticsItem;
import drds.plus.sql_process.utils.Filters;
import drds.plus.sql_process.utils.OptimizerUtils;
import drds.plus.util.GeneralUtil;
import drds.tools.$;

import java.util.List;
import java.util.Map;

/**
 * 索引选择
 *
 * <pre>
 * 索引选择策略：
 * 1. 根据选择条件查询，计算出开销最小
 * 2. 根据选择的列，找出全覆盖的索引 (顺序和查询顺序一致，前缀查询)
 * </pre>
 */
public class IndexChooser {
    private static final int initialScore = 10000;

    public static IndexMapping findBestIndexMetaData(TableMetaData tableMetaData, List<Item> itemList, List<Filter> filterList, String tableName, Map<String, Object> extraCmd) {
        if (!chooseIndex(extraCmd)) {
            return null;
        }
        List<IndexMapping> indexMappingList = tableMetaData.getIndexMetaDataList();
        if (indexMappingList.isEmpty()) {
            return null;
        }
        int[] scores = new int[indexMappingList.size()];
        for (int i = 0; i < scores.length; i++) {
            scores[i] = initialScore;
        }
        Map<Object, List<Filter>> columnToFilterListMap = Filters.toColumnToFilterListMap(filterList);
        for (int i = 0; i < indexMappingList.size(); i++) {
            // 目前不使用弱一致索引
            if (!indexMappingList.get(i).isStronglyConsistent()) {
                scores[i] = Integer.MAX_VALUE;
                continue;
            }
            TableIndexStatisticsItem tableIndexStatisticsItem = OptimizerContext.getOptimizerContext().getStatisticsManager().getIndexStatistics(indexMappingList.get(i).getIndexName());
            List<Item> indexItemList = OptimizerUtils.columnMetaListToIColumnList(indexMappingList.get(i).getKeyColumnMetaDataList(), tableName);
            for (int j = 0; j < indexItemList.size(); j++) {
                boolean isContain = false;
                if (columnToFilterListMap.isEmpty()) {// 此时以columns为准
                    isContain = itemList.contains(indexItemList.get(j));
                } else {
                    isContain = columnToFilterListMap.containsKey(indexItemList.get(j));
                }
                if (isContain) {
                    scores[i] = (int) CostEsitimaterFactory.estimateRowCount(scores[i], columnToFilterListMap.get((indexItemList.get(j))), indexMappingList.get(i), tableIndexStatisticsItem);
                    scores[i] -= 1; // 命中一个主键字段
                } else {
                    break;
                }

            }
        }
        for (int i = 0; i < scores.length; i++) {
            scores[i] = initialScore - scores[i];
        }
        int maxScoreIndex = 0;
        int maxScore = scores[maxScoreIndex];
        for (int i = 1; i < scores.length; i++) {
            if (scores[i] > maxScore) {
                maxScoreIndex = i;
                maxScore = scores[i];
            }
        }
        if (maxScore == 0) {
            return null;
        }
        return indexMappingList.get(maxScoreIndex);
    }

    /**
     * 根据查询字段，查找一个索引包含所有选择列，并且包含的无关列最少则选择该索引
     */
    public static IndexMapping findBestIndexByAllColumnsSelected(TableMetaData tableMetaData, List<Item> itemList, Map<String, Object> extraCmd) {
        if (!chooseIndex(extraCmd)) {
            return null;
        }
        IndexMapping indexMapping = null;
        int theLeastColumnsNumber = Integer.MAX_VALUE;
        List<IndexMapping> indexMappingList = tableMetaData.getIndexMetaDataList();
        for (int i = 0; i < indexMappingList.size(); i++) {
            List<Item> indexItemList = OptimizerUtils.columnMetaListToIColumnList(indexMappingList.get(i).getKeyColumnMetaDataList());
            if (indexItemList.containsAll(itemList)) {
                if (theLeastColumnsNumber > indexMappingList.get(i).getKeyColumnMetaDataList().size()) {
                    theLeastColumnsNumber = indexMappingList.get(i).getKeyColumnMetaDataList().size();
                    indexMapping = indexMappingList.get(i);
                }
            }
        }
        return indexMapping;
    }

    private static boolean chooseIndex(Map<String, Object> extraCmd) {
        String chooseIndex = GeneralUtil.getExtraCmdString(extraCmd, ConnectionProperties.CHOOSE_INDEX);
        // 默认返回true
        if ($.isNullOrEmpty(chooseIndex)) {
            return true;
        } else {
            return Boolean.parseBoolean(chooseIndex);
        }
    }

}
